﻿using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Ns
{
    //http://msdn.microsoft.com/en-us/library/ms254976.aspx
    /// <summary>
    /// Represents a GUID to be stored in or retrieved from a database.
    /// </summary>
    public static class SequentialGuid
    {
        [DllImport("rpcrt4.dll", SetLastError = true)]
        static extern int UuidCreateSequential(byte[] buffer);

        /// <summary>
        /// Initializes a new instance of a sequential <see cref="System.Guid"/> according to the SQL ordering.
        /// </summary>
        /// <returns>A new sequential GUID object.</returns>
        /// <seealso href="http://stackoverflow.com/questions/170346/what-are-the-performance-improvement-of-sequential-guid-over-standard-guid">What are the performance improvement of Sequential Guid over standard Guid?</seealso>
        /// <seealso href="http://msdn.microsoft.com/en-us/library/ms254976.aspx">Comparing GUID and uniqueidentifier Values (ADO.NET)</seealso>
        public static Guid NewGuid()
        {
            var raw = new byte[16];
            if (UuidCreateSequential(raw) != 0)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            return new Guid(ReorderedGuid(raw));
        }

        private static byte[] ReorderedGuid(byte[] value)
        {
            var fix = new byte[16];

            // reverse 0..3
            fix[0x0] = value[0x3];
            fix[0x1] = value[0x2];
            fix[0x2] = value[0x1];
            fix[0x3] = value[0x0];

            // reverse 4 & 5
            fix[0x4] = value[0x5];
            fix[0x5] = value[0x4];

            // reverse 6 & 7
            fix[0x6] = value[0x7];
            fix[0x7] = value[0x6];

            // all other are unchanged
            fix[0x8] = value[0x8];
            fix[0x9] = value[0x9];
            fix[0xA] = value[0xA];
            fix[0xB] = value[0xB];
            fix[0xC] = value[0xC];
            fix[0xD] = value[0xD];
            fix[0xE] = value[0xE];
            fix[0xF] = value[0xF];

            return fix;
        }
    }
}